;=================================================
; unit test framework sample
;

;=================================================
; here can be any function - this introduced just as a sample
(defun my-plus (&rest args)
 "this is a sample test function, which is actually the same `+' operator"
 ; moduling plus
 (eval (append (list '+) args))
 )

;=================================================
; show exec-result
(defmacro trace-fn (&rest func-with-args)
 ; defun also could be here, but you'd take in mind that the function
 ; name should be preceded with quote, otherwise the name will be
 ; interpreted as a variable
 (format t "~a -> ~a~%" func-with-args (eval func-with-args))
 )

;=================================================
; in test framework it should be possible to pass
; - a function
; - a data to this function
; - the desired result
; returns t in case the function succeeds
(defun test-given-func (func-result desired-result func-text)
 (if (= func-result desired-result)
   (progn (format t "passed: ~a -> ~a~%" func-text func-result) t)
   (progn (format t "FAILED: ~a -> ~a, not ~a~%"
        func-text func-result desired-result) nil)
  )
 )

;=================================================
; a single test-case macro
;
(defmacro test-func (func desired-result)
 `(test-given-func ,func ,desired-result ',func)
 )

;=================================================
; a multiple test function
; Syn.:
; (test-multiple {func-name} {{(({args}) {result})}...})
(defun test-multiple-func (func-name arg-result-list)
 (setf number-of-fails 0)
 (dolist (arg-result arg-result-list)
  ; reconstruct function call
  (setq func-call (append (list func-name) (first arg-result)))
  ; test the given function
  (if (not
       (test-given-func (eval func-call) (second arg-result) func-call))
   (setf number-of-fails (1+ number-of-fails)))
  ) ; dolist

 (if (> number-of-fails 0) (format t "TOTAL FAILURES: ~a~%" number-of-fails)
  (format t "Testcase succeeded.~%"))

 ;(list number-of-fails)
 ) ; test-multiple

;=================================================
; wrapper around test-multiple-func
; note, that
;           ',expr
; is the same as
;           (quote ,expr)
(defmacro test-multiple-m (func-name &rest arg-result-list)
 `(test-multiple-func ',func-name ',arg-result-list)
 )


;=================================================
; test
;
(defun test ()
 ; 1
 ;(trace-fn my-plus 1 2 3)
; (test-func (my-plus 1 2) 3)
; (test-func (my-plus 5 6 10) 2)

 ; 2
 (test-multiple-m
  my-plus           ; func-name
    ((1 2) 3)       ; test-case 1
    ((1 2 5) 8)     ; test-case 2
    ((10 20) 31)    ; wrong test case
    (nil 0)
  )

 )

;=================================================
; app entry point
;
(defun main ()

 (format t "unit test framework sample~%=====~%")

 (test)

 (exit)
 )

;=================================================
; executes main
;
(main)

